package modelo.Trayectoria;

import modelo.Orientacion.*;
import java.util.ArrayList;

public class MotorDeTrayectoria {

    private CreadorDeDirecciones creadorDir;

    public MotorDeTrayectoria(){
        creadorDir = new CreadorDeDirecciones();
    }

    private Posicion devolverPosicionAlQueSePuedeLLegar(Direccion dir, Posicion desde, Posicion fin){
    /*PreCondicion: el motor debe estar creado y la direccion debe ser combinada
      PostCondicion: devuelve la posicion que coincide con alguna alguna direccion en el eje X o en el eje Y
      que se puede llegar con la direccion dir desde la posicion Desde*/
        Posicion posSiguiente = desde;
        while (!(posSiguiente.coincideConElEjeX(fin) || posSiguiente.coincideConElEjeY(fin))){
            posSiguiente = dir.devolverSiguientePosicion(posSiguiente);
        }
        return posSiguiente;
    }

    private Direccionable crearSegmentoCombinado(Posicion inicial, Posicion fin){
    /*PreCondicion: el motor debe estar creado y debe existe alguna direccion combinada que se pueda llegar
      desde el Inicial hasta alguna posicion que coincida con el eje X o el Y de la posicion Final
      PostCondicion: te devuelve un segmento combinado desde la posicion inicial hasta alguna posicion que coincida
      con el eje X o el Y de la posicion Final*/
        Direccion dir = creadorDir.devolverDireccionCombinada(inicial, fin);
        SegmentoFijo segmento;
        Posicion posFinal = this.devolverPosicionAlQueSePuedeLLegar(dir, inicial, fin);
        segmento = new SegmentoFijo(inicial, posFinal, dir);
        return segmento;
    }

    private Direccionable crearSegmentoSimple(Posicion inicial, Posicion fin){
    /*PreCondicion: el motor debe estar creado y el inicio no debe ser igual al fin
      PostCondicion: te devuelve un segmento fijo que va desde la posicion inicial hasta el final*/
        Direccion dir;
        Direccionable segmento;
        if (creadorDir.existeAlgunaDireccionEnElEjeX(inicial, fin)){
            dir = creadorDir.devolverDireccionDelEjeX(inicial, fin);
        }else{
            dir = creadorDir.devolverDireccionDelEjeY(inicial, fin);
        }
        segmento = new SegmentoFijo(inicial, fin, dir);
        return segmento;
    }

    private void agregarSegmentoCombinado(Posicion inicial, Posicion fin, ArrayList<Direccionable> lista){
    /*PreCondicion: el motor debe estar creado
      PostCondicion: si existe una direccion combinada desde la posicion inicial hasta el final entonces
      crea un segmento y la agrega a la lista pasada por parametro, en caso contrario no hace nada */
        if (creadorDir.existeDirecionesCombinadas(inicial, fin)){
            Direccionable segmento = this.crearSegmentoCombinado(inicial, fin);
            lista.add(segmento);
        }
    }

    private void agregarSegmentoHastaElFinal(Posicion inicial, Posicion fin, ArrayList<Direccionable> lista){
    /*PreCondicion: el motor debe estar creado
      PostCondicion: si la lista esta vacia crea un segmento fijo desde la posicion Inicial y final pasadas
      por parametro, en caso contrario busca la posicion final del ultimo segmento de la lista y crea un
      segmento con esa posicion como inicio y el final pasado por parametro*/
        Direccionable segmento;
        Posicion inicio = inicial;
        if (lista.size() != 0){
            segmento = lista.get(0);
            inicio = ((SegmentoFijo)segmento).devolverElFin();
        }
        if (inicio.esIgual(fin) == false){
            segmento = this.crearSegmentoSimple(inicio, fin);
            lista.add(segmento);
        }
    }

    public ArrayList<Direccionable> devolverSegmentos(Posicion inicial, Posicion fin){
    /*PreCondicion: el motor debe estar creado
      PostCondicion: devuelve una lista de segmentos con los que se puede llegar desde la posicion inicial
      hasta la final*/
        ArrayList<Direccionable> lista = new ArrayList<Direccionable>();
        this.agregarSegmentoCombinado(inicial, fin, lista);
        this.agregarSegmentoHastaElFinal(inicial, fin, lista);
        return lista;
    }

     public Direccionable devolverSegmentoOpuesto(Direccionable segmento){
    /*PreCondicion: el motor debe estar creada
      PostCondicion: devuelve un direccionable, SegmentoSinFin, con la direccion opuesta al segmento pasado por parametro*/
        Posicion pos = segmento.devolverPosicionActual();
        Direccion dir = segmento.devolverDireccion();
        Direccion dirOpuesta = dir.devolverDireccionContraria();
        Direccionable segmentoInverso = new SegmentoConDireccion(pos, dirOpuesta);
        return segmentoInverso;
    }
     
}
